/*
 * You may freely copy, distribute and reuse the code in this example.  
 * NeXT disclaims any warranty of any kind, expressed or implied, as to 
 * its fitness for any particular use.
 */


#import "SoundDocument.h"
#import "SoundController.h"
#import "ScrollingSound.h"
#import <soundkit/soundkit.h>
#import <appkit/Application.h>
#import <appkit/Window.h>
#import <appkit/Panel.h>
#import <appkit/Cursor.h>
#import <stdlib.h>
#import <string.h>
#import <objc/NXStringTable.h>

extern int access();

@implementation SoundDocument

static void newLocation(NXPoint *p)
{
    static count = 0;
    p->x += (20.0 * count);
    p->y -= (25.0 * count);
    count = (count > 10)? 0 : count+1;
}

- init
{
    NXRect theFrame;
    
    [super init];
    [NXApp loadNibSection:"SoundDocument.nib" owner:self];
    [window setDelegate:self];
    [window getFrame:&theFrame];
    newLocation(&theFrame.origin);
    [window moveTo:theFrame.origin.x :theFrame.origin.y];
    [window makeKeyAndOrderFront:nil];
    mySoundView = [view docView];  	/* view is the ScrollingSound 
					   in the nib file */
    [mySoundView setSound:[[Sound alloc] init]];
    return self;
}


- setFileName:(char *)aName
{
    if (fileName)
	free(fileName);
    fileName = (char *)malloc(strlen(aName)+1);
    strcpy(fileName,aName);
    [window setTitleAsFilename:fileName];
    return self;
}

- (char *)fileName
{
    return fileName;
}

- sound
{
    return [mySoundView sound];
}

- saveError:(const char *)msg arg: (char *)arg;
{
    if (!stringTable)
	stringTable = [[NXApp delegate] stringTable];
    NXRunAlertPanel([stringTable valueForStringKey:"Save"],
	[stringTable valueForStringKey:msg],
	[stringTable valueForStringKey:"OK"],
	NULL,NULL,arg);
    return nil;
}

- saveToFormat:templateSound fileName:(char *)fn
{
    if (fn) {
	int err;
	id theSound = [[mySoundView sound] copy];
	if (templateSound && theSound) {
	    [theSound copySound:theSound];
	    err = [theSound convertToFormat:[templateSound dataFormat]
			    samplingRate:[templateSound samplingRate]
			    channelCount:[templateSound channelCount]];
	    if (err) {
		/* The DSP is required for compression or decompression */
		return [self saveError: 
			"Cannot do format conversion %s (DSP busy?)" arg:""];
	    }
	}
	if (!access(fn, 0)) {   /* file exists */
	    char fileBuf[1024];
	    strcpy(fileBuf,fn);
	    strcat(fileBuf,"~");
	    rename(fn,fileBuf);
	}
	err = [theSound writeSoundfile:fn];
	if (err) {
	    return [self saveError:"Cannot write %s" arg:fn];
	}
	else 
	  [window setDocEdited:NO];
	[theSound free];
    }
    return self;
}

- save:sender
{
    return [self saveToFormat:nil fileName:fileName];
}

- load:sender
{
    if (fileName) {
	id newSound, oldSound = [mySoundView sound];
	newSound = [[Sound alloc] initFromSoundfile:fileName];
	if (newSound) {
	    if (![mySoundView setSound:newSound]) { /* not displayable */
		if ([newSound convertToFormat:SND_FORMAT_LINEAR_16]) {
		    if (!stringTable)
		      stringTable = [[NXApp delegate] stringTable];
		    NXRunAlertPanel([stringTable valueForStringKey:"Open"],
				    [stringTable valueForStringKey:
				     "Cannot convert format for display "
				     "(DSP busy?)"],
				    [stringTable valueForStringKey:"OK"],
				    NULL,NULL);
		    return nil;
		} else
		  [mySoundView setSound:newSound];
	    }
	    [oldSound free];
	}
    }
    [window setDocEdited:NO];
    return self;
}

- revertToSaved:sender
{
    if([window isDocEdited] && fileName
            && strcmp(strrchr (fileName,'/'),"/UNTITLED")) {
	if (!stringTable)
	    stringTable = [[NXApp delegate] stringTable];
	if (NXRunAlertPanel([stringTable valueForStringKey:"Revert"], 		
		[stringTable valueForStringKey:"Revert to saved version"
		    " of %s?"],
		[stringTable valueForStringKey:"Revert"],
		[stringTable valueForStringKey:"Cancel"], 
		    0,fileName) == NX_ALERTDEFAULT)
	    [self load:nil];
    }
    return self;
}
  
- play:sender
{
    [mySoundView play:sender];
    return self;
}

- stop:sender
{
    [mySoundView stop:sender];
    return self;
}

- pause:sender
{
    [mySoundView pause:sender];
    return self;
}

- resume:sender
{
    [mySoundView resume:sender];
    return self;
}

- record:sender
{
    [mySoundView record:sender];
    [window setDocEdited:YES];
    return self;
}

- touch
{
    [window setDocEdited:YES];
    return self;
}

- setDelegate:anObject
{
    [mySoundView setDelegate:anObject];
    return self;
}

- windowDidBecomeMain:sender
{
    [[NXApp delegate] setDocument:self];
    [window makeFirstResponder:mySoundView];
    [mySoundView showCursor];
    return self;
}

- windowDidResignMain:sender
{
    id theController = [NXApp delegate];
    if ([theController document] == self)
	[theController setDocument:nil];
    [mySoundView hideCursor];
    return self;
}

- windowDidMiniaturize:sender
{
    [mySoundView stop:sender];
    [mySoundView hideCursor];
    return self;
}

- windowWillClose:sender
{
    int choice;

    if ([window isDocEdited]) {
	if (!stringTable)
	    stringTable = [[NXApp delegate] stringTable];
	choice = NXRunAlertPanel([stringTable valueForStringKey:"Close"],
            [stringTable valueForStringKey:"Sound is modified.\nSave it?"],
	    [stringTable valueForStringKey:"Yes"],
	    [stringTable valueForStringKey:"No"],
	    [stringTable valueForStringKey:"Cancel"]); 
	switch (choice) {
	    case NX_ALERTALTERNATE:
		break;
	    case NX_ALERTDEFAULT:
		[[NXApp delegate] save:nil];
		break;
	    case NX_ALERTOTHER:
		return nil;
	}
    }
    [self windowDidResignMain:nil];
    [window setDelegate:nil];
    [self free];
    return sender;
}


static int calcFormat(SNDSoundStruct *s)
{
    if (s->dataFormat == SND_FORMAT_MULAW_8)
	return SND_FORMAT_MULAW_8;
    else if (s->dataFormat == SND_FORMAT_INDIRECT) {
	SNDSoundStruct **iBlock = (SNDSoundStruct **)s->dataLocation;

	if (*iBlock)
	    return (*iBlock)->dataFormat;
	else
	    return SND_FORMAT_UNSPECIFIED;
    } else
	return s->dataFormat;
}

- (BOOL)isRecordable
{
    int format;
    id theSound = [mySoundView sound];
    SNDSoundStruct *soundStruct = [theSound soundStruct];

    if (!soundStruct) return YES;
    format = calcFormat(soundStruct);
    if (format == SND_FORMAT_MULAW_8 &&
    	soundStruct->samplingRate == (int)SND_RATE_CODEC &&
	soundStruct->channelCount == 1 )
	return YES;
    else
	return NO;
}

- (BOOL) isPlayable
{
    return [mySoundView isPlayable];
}

@end
